玩清一色麻将天天点人和?
这个程序是拿来计算亲家第一打点人和概率用的
实现方式就是大力枚举手牌,从以下数据可以看出枚举是很容易的
- 本质不同的 张配牌 种
- 本质不同的 张配牌 种
- 本质不同的 张配牌 种
实际上限制了一家配牌枚举另一家通常只有几千种
(顺便这个样例可以产生最大的输出 )
// 清一色麻人和率计算器 by Zhengfourth
// 命令格式:14个字符描述亲家手牌,例如:renhe 11115588889999
// 或者9个字符描述每种牌的数量,例如:renhe 400020044
// 输出表示如果你切出每一张牌对应的铳率
#include <cstdio>
#include <cstring>
#include <cstdlib>
[[noreturn]] void __ILLEGAL(int line) {
printf("%d:不合法的输入", line);
exit(-1);
}
#define ILLEGAL() __ILLEGAL(__LINE__)
const int c[5][5] = {{1},{1,1},{1,2,1},{1,3,3,1},{1,4,6,4,1}};
int a[9], b[9], ans[9];
bool dfsH(int n, int i, bool sh) {
if (!n) return true;
if (i == 9) return false;
if (!b[i]) return dfsH(n, i+1, false);
if (!sh && b[i] >= 3) {
b[i] -= 3;
bool ret = dfsH(n-3, i, true);
b[i] += 3;
if (ret) return true;
}
if (!sh && b[i] >= 2 && n % 3 == 2) {
b[i] -= 2;
bool ret = dfsH(n-2, i, true);
b[i] += 2;
if (ret) return true;
}
if (i < 7 && b[i+1] >= 1 && b[i+2] >= 1) {
b[i]--; b[i+1]--; b[i+2]--;
bool ret = dfsH(n-3, i, true);
b[i]++; b[i+1]++; b[i+2]++;
if (ret) return true;
}
return false;
}
bool check() {
bool qit = true;
for (int j = 0; j < 9; j++)
if (b[j] != 2 && b[j] != 0) qit = false;
if (qit) return true;
return dfsH(14, 0, false);
}
void test(int nc) {
for (int i = 0; i < 9; i++) if (a[i]) {
b[i]++;
if (check()) ans[i] += nc;
b[i]--;
}
}
void dfsP(int i, int x, int nc) {
if (i == 9) {
if (x == 13) test(nc);
return;
}
for (b[i] = 0; b[i]+a[i] <= 4 && x+b[i] <= 13; b[i]++) {
dfsP(i+1, x+b[i], nc*c[4-a[i]][b[i]]);
}
}
int main(int argc, char** argv) {
if (argc != 2) ILLEGAL();
char *in = argv[1];
int len = strlen(in);
if (len == 14) {
for (int i = 0; i < 14; i++) {
if (in[i] < '1' && in[i] > '9') ILLEGAL();
a[in[i]-'1']++;
}
} else if (len == 9) {
int all = 0;
for (int i = 0; i < 9; i++) {
if (in[i] < '0' && in[i] > '4') ILLEGAL();
a[i] = in[i] - '0';
all += a[i];
}
if (all != 14) ILLEGAL();
} else ILLEGAL();
dfsP(0, 0, 1);
int q = 497420;
for (int i = 0; i < 9; i++) if (a[i]) {
printf("%d: %6d/497420 = %lf\n", i+1, ans[i], 1.0d*ans[i]/q);
}
}